package org.anyin.gitee.cloud.center.gateway.handler;


import lombok.extern.slf4j.Slf4j;
import org.anyin.gitee.cloud.center.common.constants.GatewayConstants;
import org.anyin.gitee.cloud.lib.core.cache.CacheTemplate;
import org.anyin.gitee.cloud.lib.core.enums.CommonExCodeEnum;
import org.anyin.gitee.cloud.lib.core.web.ApiResponse;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class ImageCodHandler implements HandlerFunction<ServerResponse>, InitializingBean {


    @Autowired
    private CacheTemplate cacheTemplate;

    /**
     * 验证码图片的宽度。
     */
    private int width = 100;

    /**
     *  验证码图片的高度。
     */
    private int height = 30;


    /**
     * 字体高度
     */
    private int fontHeight;


    /**
     * 第一个字符的x轴值，因为后面的字符坐标依次递增，所以它们的x轴值是codeX的倍数
     */
    private int codeX;

    /**
     * codeY ,验证字符的y轴值，因为并行所以值一样
     */
    private int codeY;


    /**
     * 验证码字符个数
     */
    private int codeCount = 5;


    @Override
    public Mono<ServerResponse> handle(ServerRequest serverRequest) {
        Random random = new Random();
        int firstInt = random.nextInt(10);
        String operator = getOperator();
        int secondInt = random.nextInt(10);
        String text = calculateResult(firstInt, secondInt, operator) + "";
        BufferedImage image = this.getCaptcha(firstInt, operator, secondInt, random);

        Optional<String> optionalStr = serverRequest.queryParam("randomStr");
        if(optionalStr.isPresent()){
            String randomStr = optionalStr.get();
            cacheTemplate.valueSet(GatewayConstants.SYS_GATEWAY_CAPTCHA_KEY + randomStr, text, 5L, TimeUnit.MINUTES);
        }

        // 转换流信息写出
        FastByteArrayOutputStream os = new FastByteArrayOutputStream();
        try {
            ImageIO.write(image, "png", os);
        } catch (IOException e) {
            log.error("ImageIO write err", e);
            return Mono.error(e);
        }

        String base64 = "data:image/jpeg;base64," + Base64Utils.encodeToString(os.toByteArray());
        ApiResponse<String> response = ApiResponse.success(base64);
        return ServerResponse
                .status(HttpStatus.OK)
                .body(BodyInserters.fromValue(response));
    }

    public BufferedImage getCaptcha(int firstInt, String operator, int secondInt, Random random ){

        // 定义图像buffer
        BufferedImage buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D gd = buffImg.createGraphics();
        gd.setColor(Color.WHITE);

        // 将图像填充为白色
        gd.setColor(Color.WHITE);
        gd.fillRect(0, 0, width, height);
        // 创建字体，字体的大小应该根据图片的高度来定。
        Font font = new Font("Fixedsys", Font.PLAIN, fontHeight);
        // 设置字体。
        gd.setFont(font);
        // 画边框。
        gd.setColor(Color.WHITE);
        gd.drawRect(0, 0, width - 1, height - 1);
        drawCaptcha(String.valueOf(firstInt), gd, random, 1);
        drawCaptcha(operator, gd, random, 2);
        drawCaptcha(String.valueOf(secondInt), gd, random, 3);
        drawCaptcha("=", gd, random, 4);
        drawCaptcha("?", gd, random, 5);
        return buffImg;
    }

    private int calculateResult(int firstInt, int secondInt, String operator){
        int result = 0;
        switch (operator) {
            case "+":
                result = firstInt + secondInt;
                break;
            case "-":
                result = firstInt - secondInt;
                break;
            case "*":
                result = firstInt * secondInt;
                break;
            default:
                throw CommonExCodeEnum.DATA_PARSE_ERROR.getException();
        }
        return result;
    }

    private void drawCaptcha(String randomString, Graphics2D gd, Random random, int sequence) {
        int red = 0, green = 0, blue = 0;
        // 产生随机的颜色分量来构造颜色值，这样输出的每位数字的颜色值都将不同。
        red = random.nextInt(255);
        green = random.nextInt(255);
        blue = random.nextInt(255);
        // 用随机产生的颜色将验证码绘制到图像中。
        gd.setColor(new Color(red, green, blue));
        gd.drawString(randomString, sequence * codeX, codeY);
    }

    private String getOperator(){
        String operator = null;
        switch (new Random().nextInt(3)) {
            case 0:
                operator = "+";
                break;
            case 1:
                operator = "-";
                break;
            case 2:
                operator = "*";
                break;
            default:
                throw CommonExCodeEnum.DATA_PARSE_ERROR.getException();
        }
        return operator;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // 从web.xml中获取初始信息
        // 宽度
        String strWidth = "100";
        // 高度
        String strHeight = "30";
        // 字符个数
        String strCodeCount = "5";
        // 将配置的信息转换成数值
        try {
            width = Integer.parseInt(strWidth);
            height = Integer.parseInt(strHeight);
            codeCount = Integer.parseInt(strCodeCount);
        } catch (NumberFormatException e) {
            log.error("unexpected exception occur, {}", e.getMessage(), e);
        }
        //width-4 除去左右多余的位置，使验证码更加集中显示，减得越多越集中。
        //codeCount+1     //等比分配显示的宽度，包括左右两边的空格
        codeX = (width - 5) / (codeCount + 1);
        //height - 10 集中显示验证码
        fontHeight = height - 10;
        codeY = height - 7;
    }
}
